package com.m4f.city.web.controller.admin;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.cache.CacheException;
import javax.validation.Valid;

import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.m4f.city.business.domain.Application;
import com.m4f.city.business.domain.Organization;
import com.m4f.city.business.security.InternalUserDetailService;
import com.m4f.city.utils.table.DataTable;
import com.m4f.city.utils.table.DataTable2;
import com.m4f.city.web.controller.BaseController;
import com.m4f.city.web.form.OrganizationForm;
import com.m4f.gaeweb.web.utils.StackTraceUtil;

@Controller
@Secured(InternalUserDetailService.ROLE_ROOT)
@RequestMapping("/admin/organization")
public class OrganizationAdminController extends BaseController {
	private static final Logger LOGGER = Logger.getLogger(OrganizationAdminController.class.getName());
	
	public OrganizationAdminController() throws CacheException {
		super();
	}
	
	@RequestMapping(method=RequestMethod.GET)
	public String getForm(Model model, Locale locale) {
		try {
			Organization org = this.serviceLocator.getOrganizationService().createOrganization();
			model.addAttribute("form", new OrganizationForm(org));
		} catch(Exception e) {
			LOGGER.severe(StackTraceUtil.getStackTrace(e));
			return "common.error";
		}
		return "admin.organization.edit";
	}
	
	@RequestMapping(method=RequestMethod.POST)
	public String save(@ModelAttribute("form") @Valid OrganizationForm form, 
			BindingResult result, Model model, Locale locale) {
		// Check if exist an user with this mail
		boolean updateAction = false;
		try {
			if(result.hasErrors()) {
				return "admin.organization.edit";
			}
			
			Organization org = form.getOrganization();
			if(org.getId() != null) updateAction = true;
			this.serviceLocator.getOrganizationService().save(org);
			if(updateAction) addSuccessMessage("message.success.admin.organization.updated");
			else addSuccessMessage("message.success.admin.organization.created");
		} catch(Exception e) {
			if(updateAction) addErrorMessage("message.error.admin.organization.updated");
			else addErrorMessage("message.error.admin.organization.created");
			LOGGER.severe(StackTraceUtil.getStackTrace(e));
			return "common.error";
		}
		
		return getRedirect("/admin/organization/list", locale);
	}
	
	@RequestMapping(value="/list", method=RequestMethod.GET)
	public String list(Model model, Locale locale,
			@RequestParam(defaultValue="1", required=false) Integer page, 
			@RequestParam(defaultValue="", required=false) String order) {
		try {
			String ordering = order != null && !("").equals(order) ? order : "name";
	
			model.addAttribute("organizations", this.serviceLocator.getOrganizationService().getAllOrganization());
			
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return "common.error";
		}
		return "admin.organization.list";
	}
	
	@RequestMapping(value="/edit/{orgId}", method=RequestMethod.GET)
	public String edit(@PathVariable Long orgId, Model model, Locale locale) {
		try {
			Organization org = this.serviceLocator.getOrganizationService().getOrganization(orgId);
			
			model.addAttribute("form", new OrganizationForm(org));
		} catch(Exception e) {
			LOGGER.severe(StackTraceUtil.getStackTrace(e));
			return "common.error";
		}
		return "admin.organization.edit";
	}
	
	@RequestMapping(value="/delete/{orgId}", method=RequestMethod.GET)
	public String delete(@PathVariable Long orgId, Model model, Locale locale) {
		try {
			Organization org = this.serviceLocator.getOrganizationService().getOrganization(orgId);
			this.serviceLocator.getOrganizationService().delete(org);
			addSuccessMessage("message.success.admin.organization.deleted");
		} catch(Exception e) {
			addErrorMessage("message.error.admin.organization.deleted");
			LOGGER.severe(StackTraceUtil.getStackTrace(e));
			return "common.error";
		}
		
		return getRedirect("/admin/organization/list", locale);
	}
	
	@RequestMapping(value="/detail/{orgId}", method=RequestMethod.GET)
	public String detail(@PathVariable Long orgId, Model model, Locale locale) {
		try {
			Organization org = this.serviceLocator.getOrganizationService().getOrganization(orgId);
			model.addAttribute("organization", org);
		} catch(Exception e) {
			LOGGER.severe(StackTraceUtil.getStackTrace(e));
			return "common.error";
		}
		return "admin.organization.detail";
	}

	// TESTING
	@RequestMapping(value="/list3", method=RequestMethod.GET)
	public String list3() {
		return "admin.organization.list3";
	}
	
	@RequestMapping(value="/list3/table")
	@ResponseBody
	public DataTable2<Organization> table3(@RequestParam(required=false) Integer iDisplayStart, @RequestParam(required=false) Integer iDisplayLength, /* Pagination */
			@RequestParam(required=false) Integer iSortCol_0, @RequestParam(required=false) String sSortDir_0, /* Sorting */
			@RequestParam(required=false, defaultValue="") String sSearch /* Search */) {
		try {
			
			// Filter 
			// TODO Now its difficult to filter by columns. So there is a general filter and here we have to set the default column to filter.
			String filterBy = "name";
			String filterValue = sSearch;
			// End filter
			
			DataTable2<Organization> table = iDisplayLength != null ?
					new DataTable2<Organization>(new String[]{"id", "name"}, sSortDir_0, iDisplayStart, iDisplayLength) :
					new DataTable2<Organization>(new String[]{"id", "name"}, sSortDir_0, iDisplayStart);
			table.setRows(this.serviceLocator.getOrganizationService().getOrganizationsByRange(table.getOrdering(iSortCol_0), filterBy, filterValue, table.getStart(), table.getEnd()));
			table.setiTotalDisplayRecords(this.serviceLocator.getOrganizationService().countOrganization());
			return table;
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return new DataTable2<Organization>();
		}
	}	
	
	@RequestMapping(value="/list2", method=RequestMethod.GET)
	public String list2() {
		return "admin.organization.list2";
	}
	
	@RequestMapping(value="/list2/table")
	@ResponseBody
	public DataTable<Organization> table(@RequestParam Integer page, @RequestParam Integer rp,
			@RequestParam(defaultValue="") String sortname, @RequestParam(defaultValue="") String sortorder,
			@RequestParam(defaultValue="") String qtype, @RequestParam(defaultValue="") String query) {
		try {
			// Order
			String ordering = "";
			if(!("").equals(sortname) && !("").equals(sortorder)) {
				if(("asc").equals(sortorder)) ordering = sortname;
				else ordering = "-" + sortname;
			}
			// Filter
			if(qtype == null || query == null) {
				qtype = "";
				query = "";
			}
			DataTable<Organization> table = new DataTable<Organization>();
			table.setPage(page);
			table.setTotal((int)this.serviceLocator.getOrganizationService().countOrganization());
			table.setCollection(this.serviceLocator.getOrganizationService().getOrganizationsByRange(ordering, qtype, query, (page-1)*rp, ((page-1)*rp)+rp));
			return table;
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return new DataTable<Organization>(new ArrayList<Organization>());
		}
	}	
	
	
	
	// APPLICATIONS MANAGEMENT
	@RequestMapping(value="/{orgId}/applications2", method=RequestMethod.GET)
	public String getApplications2(@PathVariable Long orgId) {
		return "admin.organization.applications2";
	}
	
	@RequestMapping(value="/applications2/all")
	@ResponseBody
	public DataTable2<Application> getApplicationsData2(@RequestParam Integer iDisplayStart, @RequestParam Integer iDisplayLength, /* Pagination */
			@RequestParam Integer iSortCol_0, @RequestParam String sSortDir_0, /* Sorting */
			@RequestParam Integer sSearch, @RequestParam String sSearch_0 /* Search */) {
		try {
			// Filter
			DataTable2<Application> table = new DataTable2<Application>(new String[]{"id", "name", "companyId"}, sSortDir_0, iDisplayStart, iDisplayLength);
			table.setRows(this.serviceLocator.getApplicationService().getObjectsBy(table.getOrdering(iSortCol_0), "", "", table.getStart(), table.getEnd()));
			table.setiTotalDisplayRecords(this.serviceLocator.getApplicationService().count());
			return table;
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return new DataTable2<Application>();
		}
	}
	
	@RequestMapping(value="/{orgId}/applications", method=RequestMethod.GET)
	public String getApplications(@PathVariable Long orgId) {
		return "admin.organization.applications";
	}
		
	@RequestMapping(value="/applications/all")
	@ResponseBody
	public DataTable<Application> getApplicationsData(@RequestParam Integer page, @RequestParam Integer rp,
			@RequestParam(defaultValue="") String sortname, @RequestParam(defaultValue="") String sortorder,
			@RequestParam(defaultValue="") String qtype, @RequestParam(defaultValue="") String query) {
		try {
			// Order
			String ordering = "";
			if(!("").equals(sortname) && !("").equals(sortorder)) {
				if(("asc").equals(sortorder)) ordering = sortname;
				else ordering = "-" + sortname;
			}
			// Filter
			if(qtype == null || query == null) {
				qtype = "";
				query = "";
			}
			DataTable<Application> table = new DataTable<Application>();
			table.setPage(page);
			table.setTotal((int)this.serviceLocator.getApplicationService().count());
			table.setCollection(this.serviceLocator.getApplicationService().getObjectsBy(ordering, qtype, query, (page-1)*rp, ((page-1)*rp)+rp));
			return table;
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return new DataTable<Application>(new ArrayList<Application>());
		}
	}
	
	@RequestMapping(value="/{orgId}/applications/list")
	@ResponseBody
	public List<Long> getApplicationIds(@PathVariable Long orgId) {
		try {
			// Order
			Organization org = this.serviceLocator.getOrganizationService().getOrganization(orgId);
			
			return org.getApplicationIds();
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return new ArrayList<Long>();
		}
	}
	
	@RequestMapping(value="/{orgId}/applications/add", method=RequestMethod.POST)
	@ResponseBody
	public boolean addApplicationToOrganization(@PathVariable Long orgId, @RequestParam Long appId) {
		try {
			Application app = this.serviceLocator.getApplicationService().getObjectById(appId);
			Organization org = this.serviceLocator.getOrganizationService().getOrganization(orgId);
			if(!org.getApplicationIds().contains(app.getId())) {
				org.getApplicationIds().add(app.getId());
				this.serviceLocator.getOrganizationService().save(org);
			}
			return true;
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return false;
		}
	}
	
	@RequestMapping(value="/{orgId}/applications/delete", method=RequestMethod.POST)
	@ResponseBody
	public boolean deleteApplicationFromOrganization(@PathVariable Long orgId, @RequestParam Long appId) {
		try {
			Application app = this.serviceLocator.getApplicationService().getObjectById(appId);
			Organization org = this.serviceLocator.getOrganizationService().getOrganization(orgId);
			if(org.getApplicationIds().contains(app.getId())) {
				org.getApplicationIds().remove(app.getId());
				this.serviceLocator.getOrganizationService().save(org);
			}
			return true;
		} catch(Exception e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
			return false;
		}
	}
}